
#include <iostream>
#include <printer.h>
#include <ast.h>
#include <type.h>

void Printer::gen(BlockStat *ptr) {
    for (Stat& st : ptr->stats) {
        generateStat(this, st);
    }
}

void Printer::gen(ReturnStat *ptr) {
    content.push_back("return ");
    _nested++;
    generateStat(this, ptr->stat);
    _nested--;
    content.push_back(";\n");
}

void Printer::gen(IfStat* ptr) {
    content.push_back("if (");
    generateStat(this, ptr->cond);
    content.push_back(") {\n");
    generateStat(this, ptr->true_stat);
    content.push_back("}");
    if (ptr->false_stat.has_value()) {
      content.push_back(" else {\n");
      generateStat(this, ptr->false_stat);
      content.push_back("}");
    }
}

void Printer::gen(UnaryOpStat* ptr) {
    switch (ptr->op) {
      case kOpSelfDecSufix:
      case kOpSelfIncSufix:
        generateStat(this, ptr->exp);
        content.push_back(operatorName(ptr->op));
        break;
      case kOpSelfDecPrefix:
      case kOpSelfIncPrefix:
      case kOpNegative:
      case kOpPostive:
      case kOpPtrDeref:
      case kOpAddress:
      case kOpSizeof:
        content.push_back(operatorName(ptr->op));
        if (ptr->op == kOpSizeof) content.push_back("(");
        generateStat(this, ptr->exp);
        if (ptr->op == kOpSizeof) content.push_back(")");
        break;
      default: panic("type match failed"); break;
    }
}
void Printer::gen(BinaryOpStat* ptr) {
  _nested++;
  OpAssociative asso = operatorAssociative(ptr->op);
  if (ptr->op == kOpFuncCall) {
    generateStat(this, ptr->lhs);
    content.back() += "(";
    FuncCallArgsStat stat = std::any_cast<FuncCallArgsStat>(ptr->rhs);
    size_t sarg = stat.args.size();
    for (size_t i = 0; i < sarg; i++) {
      generateStat(this, stat.args[i]); 
      if (i != sarg-1) content.back() += ",";
    }
    content.back() += ")\n";
  } else if (asso == kLeftToRight) {
    generateStat(this, ptr->lhs);
    content.push_back(operatorName(ptr->op));
    generateStat(this, ptr->rhs);
    if (ptr->op == kOpArraySubscript) content.push_back("]");
  } else {
      generateStat(this, ptr->rhs);
      content.push_back(operatorName(ptr->op));
      generateStat(this, ptr->lhs);
  }
  if (--_nested == 0) content.back() += ";\n";
}

void Printer::gen(TernaryOpStat* ptr) {
  generateStat(this, ptr->cond);
  content.push_back("?(");
  generateStat(this, ptr->true_stat);
  content.push_back("):(");
  generateStat(this, ptr->false_stat);
  content.push_back(")");
}

void Printer::gen(NameStat* ptr) {
    content.push_back(ptr->name);
}

void Printer::gen(LiteralIntegerStat* ptr) {
  content.push_back(std::to_string(ptr->val));
}

void Printer::gen(ForStat* ptr) {
  content.push_back("for (");
  _nested++;
  generateStat(this, ptr->init);
  content.push_back(";");
  generateStat(this, ptr->cond);
  content.push_back(";");
  generateStat(this, ptr->step);
  _nested--;
  content.push_back(") {\n");
  generateStat(this, ptr->stat);
  content.push_back("}\n");
}


void Printer::genType(Type type) {
  Type base = type;
  int nested = 0;
  std::vector<size_t> arrays;
  while (base.type() == typeid(TypeArray)) {
    TypeArray arr = std::any_cast<TypeArray>(base);
    arrays.push_back(arr._dlen);
    base = arr._base;
  }
  while (base.type() == typeid(TypePointer)) {
    base = std::any_cast<TypePointer>(base)._base;
    nested++;
  }

  assert(base.type() == typeid(TypeBuiltin));
  content.push_back(std::any_cast<TypeBuiltin>(base).name() + std::string(nested, '*'));

  for (size_t i = arrays.size(); i; i--) {
    content.back() += "[" + std::to_string(arrays[i-1]) + "]";
  }
  content.back() += std::to_string(sizeType(type));
}


void Printer::gen(ParamStat* ptr) {
  genType(ptr->type);
  content.back() += " " + ptr->name;
}

void Printer::gen(FunctionStat* ptr) {
  genType(ptr->ret);
  content.back() += " " + ptr->name + "(";

  size_t psize = ptr->args.size();
  for (size_t i = 0; i < psize; i++) {
    genType(ptr->args[i].first);
    content.back() += " " + ptr->args[i].second;
    if (i != psize-1) content.push_back(", ");
  }

  content.back() += "){\n";
  generateStat(this, ptr->stats);
  content.back() += "\n}";
}
void Printer::gen(VariableStat* ptr) {
  genType(ptr->type);
  content.back() += " " + ptr->name;
  if (ptr->init.type() != typeid(EmptyStat)) {
    content.back() += " = ";
    generateStat(this, ptr->init);
  }
}
void Printer::gen(VariableDefineStat* ptr) {
  for (auto& var : ptr->stat) generateStat(this, var), content.back() += ";\n";
}

void Printer::gen(StructStat *ptr) {
  TypeStruct type = std::any_cast<TypeStruct>(ptr->type);
  content.push_back("struct " + ptr->name + " {\n");

  for (auto& s : ptr->member) {
    genType(type._member[s]);
    content.back() += " " + s + "offset:" + std::to_string(type._offset[s]) + ";\n";
  }
  content.push_back("};" + std::to_string(type.size()));
}

void Printer::gen(VarStat *ptr) {
  content.push_back(ptr->name);
}

void Printer::gen(WhileStat *ptr) {}
void Printer::gen(DoWhileStat *ptr) {}
void Printer::gen(GotoStat *ptr) {
  content.push_back("goto " + ptr->name + ";\n");
}
void Printer::gen(ContinueStat *ptr) {
  content.push_back("continue;\n");
}
void Printer::gen(BreakStat *ptr) {
  content.push_back("break;\n");
}
void Printer::gen(LabelStat *ptr) {
  content.push_back(ptr->name + ":\n");
}



